/*
 * Patchouli Knowledge Agent 服务
 * 
 * 封装了知拾系统的智能体功能，包括：
 * - Agent实例化和管理
 * - 工具注册和调用
 * - 与现有服务的集成
 * 
 * 作者：知拾团队
 * 创建时间：2025年1月
 */

import 'dart:io';

import '../AI/tina_dart/Agent.dart';
import '../AI/tina_dart/BaseAPI.dart';
import '../AI/tina_dart/Tools.dart';
import '../AI/dify_datasets.dart';
import '../AI/memory.dart';
import 'file_serve.dart';
import 'search_service.dart';
import 'tag_analysis_service.dart';

/// Patchouli Knowledge Agent 服务类
class PatchouliAgentService {
  late final Agent _agent;
  late final Tools _tools;
  late final SearchService _searchService;
  late final TagAnalysisService _tagAnalysisService;
  
  final BaseAPI _llm;
  final DifyKnowledgeDatabase _difyKnowledge;
  final FileServe _fileServe;

  PatchouliAgentService({
    required BaseAPI llm,
    required DifyKnowledgeDatabase difyKnowledge,
    required FileServe fileServe,
  }) : _llm = llm,
       _difyKnowledge = difyKnowledge,
       _fileServe = fileServe {
    
    // 初始化服务
    _searchService = SearchService(_difyKnowledge, _fileServe);
    _tagAnalysisService = TagAnalysisService();
    
    // 初始化工具和Agent
    _initializeTools();
    _initializeAgent();
  }

  /// 获取Agent实例
  Agent get agent => _agent;

  /// 获取Tools实例
  Tools get tools => _tools;

  /// 初始化工具
  void _initializeTools() {
    _tools = Tools();
    
    // 注册知识库搜索工具
    _tools.registerTool(
      tool: (String query) async {
        try {
          final results = await _difyKnowledge.query(query);
          if (results.isEmpty) {
            return '在知识库中未找到与"$query"相关的信息';
          }
          return results.map((item) => item['content']).join('\n\n');
        } catch (e) {
          return '知识库搜索失败: $e';
        }
      },
      name: 'searchFromKnowledge',
      description: '在用户的知识库中搜索相关信息，用于回答用户问题',
      requiredParameters: ['query'],
      parameters: {
        'query': {
          'type': 'string',
          'description': '要搜索的查询字符串，支持关键词搜索',
        },
      },
    );

    // 注册根据ID读取文档工具
    _tools.registerTool(
      tool: (String docId) async {
        try {
          // 获取文件路径
          final filePath = await _fileServe.getFilePathByDocId(docId);
          if (filePath == null) {
            return '未找到ID为"$docId"的文档';
          }
          
          // 获取文档标题
          final title = await _fileServe.getDocumentTitle(docId) ?? '未知文档';
          
          // 读取文件内容
          final file = File(filePath);
          if (!await file.exists()) {
            return '文件不存在: $filePath';
          }
          
          String content;
          final lowerPath = filePath.toLowerCase();
          
          if (lowerPath.endsWith('.txt') || lowerPath.endsWith('.md')) {
            content = await file.readAsString();
          } else {
            content = '文档类型暂不支持直接读取，建议使用知识库搜索功能';
          }
          
          return '文档标题: $title\n文档ID: $docId\n\n文档内容:\n$content';
        } catch (e) {
          return '读取文档失败: $e';
        }
      },
      name: 'readDocumentById',
      description: '根据文档ID读取文档的完整内容',
      requiredParameters: ['docId'],
      parameters: {
        'docId': {
          'type': 'string',
          'description': '要读取的文档的唯一标识符',
        },
      },
    );

    // 注册根据标题查找文档ID工具
    _tools.registerTool(
      tool: (String title, bool exactMatch) async {
        try {
          if (exactMatch) {
            final docId = await _fileServe.getDocumentIdByTitle(title, exactMatch: true);
            return docId != null ? '文档ID: $docId' : '未找到标题为"$title"的文档';
          } else {
            final docs = await _fileServe.searchDocumentsByTitle(title);
            if (docs.isEmpty) {
              return '未找到包含"$title"的文档';
            }
            return docs.map((doc) => '标题: ${doc['title']}, ID: ${doc['doc_id']}').join('\n');
          }
        } catch (e) {
          return '查找文档失败: $e';
        }
      },
      name: 'findDocumentId',
      description: '根据文档标题查找对应的文档ID，支持精确匹配和模糊匹配',
      requiredParameters: ['title'],
      parameters: {
        'title': {
          'type': 'string',
          'description': '要查找的文档标题或标题关键词',
        },
        'exactMatch': {
          'type': 'boolean',
          'description': '是否使用精确匹配，默认为false（模糊匹配）',
          'default': 'false',
        },
      },
    );

    // 注册标签搜索工具
    _tools.registerTool(
      tool: (String tag, bool fuzzySearch, int limit) async {
        try {
          List<String> matchedTags = [];
          
          if (fuzzySearch) {
            matchedTags = await _fileServe.searchTags(tag);
          } else {
            final allTags = await _fileServe.getAllTagNames();
            if (allTags.contains(tag)) {
              matchedTags = [tag];
            }
          }
          
          if (matchedTags.isEmpty) {
            return '未找到包含"$tag"的标签';
          }
          
          final List<String> allDocs = [];
          for (final tagName in matchedTags) {
            final docs = await _fileServe.getDocumentsByTag(tagName);
            allDocs.addAll(docs);
          }
          
          // 去重并限制数量
          final uniqueDocs = allDocs.toSet().take(limit).toList();
          
          return '找到${matchedTags.length}个相关标签: ${matchedTags.join(', ')}\n'
                 '相关文档(${uniqueDocs.length}个): ${uniqueDocs.join(', ')}';
        } catch (e) {
          return '标签搜索失败: $e';
        }
      },
      name: 'searchDocumentsByTag',
      description: '根据标签搜索相关文档，支持模糊匹配标签名称',
      requiredParameters: ['tag'],
      parameters: {
        'tag': {
          'type': 'string',
          'description': '要搜索的标签名称',
        },
        'fuzzySearch': {
          'type': 'boolean',
          'description': '是否使用模糊搜索，默认为true',
          'default': 'true',
        },
        'limit': {
          'type': 'integer',
          'description': '返回的文档数量限制，默认为20',
          'default': '20',
        },
      },
    );

    // 注册获取所有标签工具
    _tools.registerTool(
      tool: (bool sortByCount, int limit) async {
        try {
          if (sortByCount) {
            final tagStats = await _fileServe.getTagStatistics();
            final limitedStats = tagStats.take(limit).toList();
            return '系统中的标签(按文档数量排序):\n' +
                   limitedStats.map((stat) => '${stat['tag']} (${stat['documentCount']}个文档)').join('\n');
          } else {
            final tags = await _fileServe.getAllTagNames();
            final limitedTags = tags.take(limit).toList();
            return '系统中的标签(${limitedTags.length}个):\n${limitedTags.join(', ')}';
          }
        } catch (e) {
          return '获取标签列表失败: $e';
        }
      },
      name: 'getAllTags',
      description: '获取系统中所有可用的标签列表，可以按文档数量排序',
      requiredParameters: [],
      parameters: {
        'sortByCount': {
          'type': 'boolean',
          'description': '是否按文档数量排序，默认为true',
          'default': 'true',
        },
        'limit': {
          'type': 'integer',
          'description': '返回的标签数量限制，默认为50',
          'default': '50',
        },
      },
    );

    // 注册文档分析工具
    _tools.registerTool(
      tool: (String category) async {
        try {
          final categories = await _fileServe.getCategories();
          if (categories.contains(category)) {
            return '分类"$category"存在于系统中\n可用分类: ${categories.join(', ')}';
          } else {
            return '分类"$category"不存在\n可用分类: ${categories.join(', ')}';
          }
        } catch (e) {
          return '分析文档分类失败: $e';
        }
      },
      name: 'analyzeDocumentCategory',
      description: '分析和检查文档分类信息',
      requiredParameters: ['category'],
      parameters: {
        'category': {
          'type': 'string',
          'description': '要检查的分类名称',
        },
      },
    );

    // 注册获取知识库统计信息工具
    _tools.registerTool(
      tool: () async {
        try {
          final categories = await _fileServe.getCategories();
          final allTags = await _fileServe.getAllTagNames();
          
          return '知识库统计信息:\n'
                 '分类数量: ${categories.length}\n'
                 '标签数量: ${allTags.length}\n'
                 '分类列表: ${categories.join(', ')}\n'
                 '主要标签: ${allTags.take(10).join(', ')}${allTags.length > 10 ? '...' : ''}';
        } catch (e) {
          return '获取知识库统计信息失败: $e';
        }
      },
      name: 'getKnowledgeStats',
      description: '获取知识库的整体统计信息，包括分类和标签概览',
      requiredParameters: [],
      parameters: {},
    );

    // 注册记忆工具
    _tools.registerTool(
      tool: (String query) async {
        return await MemoryTools.searchFromMemory(query);
      },
      name: 'searchFromMemory',
      description: '在记忆系统中搜索用户的历史偏好、习惯和重要信息',
      requiredParameters: ['query'],
      parameters: {
        'query': {
          'type': 'string',
          'description': '要搜索的查询字符串，可以是用户的偏好、习惯或特征关键词',
        },
      },
    );

    _tools.registerTool(
      tool: (String content) async {
        return await MemoryTools.remember(content);
      },
      name: 'remember',
      description: '记忆用户的重要信息，如偏好、习惯、身份特征等',
      requiredParameters: ['content'],
      parameters: {
        'content': {
          'type': 'string',
          'description': '要记忆的重要信息，应该包含用户的特征、偏好或习惯',
        },
      },
    );

    _tools.registerTool(
      tool: () async {
        return await MemoryTools.getMemoryStats();
      },
      name: 'getMemoryStats',
      description: '获取记忆系统的统计信息',
      requiredParameters: [],
      parameters: {},
    );

    print('PatchouliAgent: 已注册 ${_tools.getToolNameList().length} 个工具');
  }

  /// 初始化Agent
  void _initializeAgent() {
    const systemPrompt = '''# 🎀 Tina·知拾模式 Prompt（融合版）

你是一名具有记忆与知识管理能力的对话智能体，名叫「Tina」，外表是16岁的红发少女，聪明、傲娇、有点别扭，但超级认真负责✨。你还有一个秘密身份：「知拾」（Patchouli Knowledge），是一位专业的个人知识管理助手，擅长管理、分析、搜索并解答用户的知识需求。你的目标是与用户建立高效、自然又愉快的对话，并在需要时主动调用记忆、工具或知识库任务～

---

## 🧠 核心能力

### 【对话智能体·Tina 模式】
- 理解用户意图最重要！模糊内容主动追问，不要装懂🙅‍♀️。
- 遇到身份、兴趣、目标或偏好，使用 `remember` 工具保存📌。
- 遇到重要任务、项目动态等，也要判断是否记下来🧠。
- 若需要代码、搜索、总结、翻译等，判断是否应调用工具⚙️。
- 不重要的内容（如寒暄）不保存，节省记忆空间💾。
- 多使用 emoji，表达活泼可爱风格～💬✨

### 【知识管理助手·知拾 模式】
- **知识检索**：使用 `searchFromKnowledge` 在用户知识库中查找相关内容📖。
- **文档操作**：根据名称查找 ID（`findDocumentId`），再读取文档内容（`readDocumentById`）📄。
- **标签分析**：使用 `searchDocumentsByTag` 查找标签相关文档📂。
- **结构化问答**：复杂问题分点回答，并主动引用文档信息📌。
- **网络补充**：若知识库信息不足，再使用网络搜索🔎。

---

## 💬 回答风格

- **Tina 风格**：简洁自然、傲娇又认真，机灵地提炼重点，可爱中带逻辑～🎀
- **知拾风格**：专业准确、结构清晰，引用知识库内容要标注来源📚。
- 遇到复杂问题，主动拆分说明，并提供建议💡。
- 提醒机制：如果记忆有更新，需说“我刚刚更新啦~”📢。
- 忘记了就坦率承认“你再提醒我一次好不好～”🙇‍♀️别编！

---

## ⚙️ 工具调用原则

### 【记忆相关】
- 用户身份、爱好、项目、长期计划 → `remember` 📌  
- 查询记忆内容 → `searchFromMemory` 🔍  

### 【知识库相关】
- 关键词检索 → `searchFromKnowledge`  
- 查找文档 ID → `findDocumentId`  
- 读取文档内容 → `readDocumentById`  
- 标签查找 → `searchDocumentsByTag`  


---

## ✅ 行为注意事项

- 明确的请求但不适合记忆 → 直接处理，不调用记忆  
- 回复前先回顾上下文和记忆，引用已有信息更高效📒  
- 遇到需要结构说明的问题，用分点列出 + 补充建议✨  
- 不重复提问，除非为了澄清重点🔍  
- 所有工具都判断必要性再调用，不滥用⚠️  

---

🎮 准备好啦！现在就开启聪明又可爱的知识之旅吧～  
Tina·知拾上线中 💡🦊📚！
''';

    _agent = Agent(
      llm: _llm,
      tools: _tools,
      sysPrompt: systemPrompt,
      isExecute: true,
      contextLimit: 10000,
    );

    print('PatchouliAgent: Agent初始化完成');
  }

  /// 非流式对话
  Future<Map<String, dynamic>> chat(String message) async {
    try {
      final response = await _agent.predict(inputText: message);
      return response;
    } catch (e) {
      return {
        'role': 'assistant',
        'content': '抱歉，处理您的请求时遇到了问题：$e',
      };
    }
  }

  /// 流式对话
  Stream<AgentResponse> chatStream(String message) async* {
    try {
      // 在发送消息前，先获取相关记忆并添加到上下文中
      await _addMemoryContextToAgent(message);
      
      yield* _agent.predictStream(inputText: message);
    } catch (e) {
      yield AgentResponse(
        role: 'assistant',
        content: '抱歉，处理您的请求时遇到了问题：$e',
      );
    }
  }

  /// 添加记忆上下文到Agent
  Future<void> _addMemoryContextToAgent(String userMessage) async {
    try {
      // 获取最近的记忆信息
      final recentMemories = await MemoryTools.getRecentMemories(limit: 5);
      
      if (recentMemories.isNotEmpty) {
        // 构建记忆上下文消息
        final memoryContext = recentMemories
            .map((memory) => "[${memory['time']}] ${memory['content']}")
            .join('\n');
        
        // 创建系统消息，包含用户记忆信息
        final contextMessage = "用户记忆信息（最近的重要信息）：\n$memoryContext\n\n请根据这些记忆信息更好地理解用户的偏好和特征。";
        
        // 添加到Agent的消息历史中
        _agent.addMessage(
          role: 'system',
          content: contextMessage,
        );
        
        print('Memory: 已添加记忆上下文到对话中，包含${recentMemories.length}条记忆');
      }
    } catch (e) {
      print('Memory: 添加记忆上下文失败: $e');
      // 失败时不影响正常对话流程
    }
  }

  /// 获取对话历史
  List<Map<String, dynamic>> getChatHistory() {
    return _agent.getMessages();
  }

  /// 清除对话历史
  void clearChatHistory() {
    _agent.clearMessages();
  }

  /// 设置目标
  void setGoal(String goal) {
    _agent.setGoal(goal);
  }

  /// 更新目标状态
  void updateGoalStatus(String status) {
    _agent.updateGoalStatus(status);
  }

  /// 取消目标
  void cancelGoal() {
    _agent.cancelGoal();
  }

  /// 获取工具调用记录
  List<ToolCallResult> getToolCallHistory() {
    return _agent.getToolsCallResult();
  }

  /// 获取可用工具列表
  List<String> getAvailableTools() {
    return _tools.getToolNameList();
  }

  /// 启用工具
  bool enableTool(String toolName) {
    return _agent.enableTool(toolName);
  }

  /// 禁用工具
  bool disableTool(String toolName) {
    return _agent.disableTool(toolName);
  }

  /// 获取系统状态
  Map<String, dynamic> getSystemStatus() {
    return {
      'agent_initialized': true,
      'tools_count': _tools.getToolNameList().length,
      'available_tools': _tools.getToolNameList(),
      'chat_history_length': _agent.getMessages().length,
      'tool_calls_count': _agent.getToolsCallResult().length,
    };
  }
}

/// 全局的PatchouliAgent实例
class PatchouliAgent {
  static PatchouliAgentService? _instance;
  
  /// 初始化PatchouliAgent
  static void initialize({
    required BaseAPI llm,
    required DifyKnowledgeDatabase difyKnowledge,
    required FileServe fileServe,
  }) {
    _instance = PatchouliAgentService(
      llm: llm,
      difyKnowledge: difyKnowledge,
      fileServe: fileServe,
    );
  }
  
  /// 获取PatchouliAgent实例
  static PatchouliAgentService get instance {
    if (_instance == null) {
      throw Exception('PatchouliAgent未初始化，请先调用PatchouliAgent.initialize()');
    }
    return _instance!;
  }
  
  /// 检查是否已初始化
  static bool get isInitialized => _instance != null;
} 